/**
 * MIT License
 *
 * Copyright (c) 2024 小村程序员
 * Email: 397237738@qq.com
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.open.cobertura.report.html;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.open.cobertura.enums.DataType;
import org.open.cobertura.model.ClassCoverage;
import org.open.cobertura.model.LineCoverage;
import org.open.cobertura.model.MethodCoverage;
import org.open.cobertura.model.PackageCoverage;
import org.open.cobertura.util.CopyFiles;
import org.open.cobertura.util.Crc64;
import org.open.cobertura.util.FileLineReaderUtil;
import org.open.cobertura.util.PathUtil;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public abstract class AbstractCreateReport {
    /**
     * 源码集合
     */
    private ConcurrentMap<String,List<String>> sourceMap;
    public AbstractCreateReport() {
        sourceMap=new ConcurrentHashMap<>();
    }

    /**
     * 输出类的覆盖报告页面
     * @param packageCoverageList 包的覆盖率数据
     * @param classCoverageMap 类的覆盖率数据
     */
    public abstract void outputClassCoverageHtml(List<PackageCoverage> packageCoverageList,Map<String, List<ClassCoverage>> classCoverageMap);

    /**
     * 输出方法的覆盖率报告页面
     * @param packageCoverageList 包的覆盖率数据
     * @param methodCoverageMap 方法的覆盖率数据
     */
    public abstract void outputMethodCoverageHtml(List<PackageCoverage> packageCoverageList,Map<String, List<MethodCoverage>> methodCoverageMap);

    /**
     * 输出源代码的覆盖率报告页面
     * @param lineCoverageMap 行的覆盖率数据
     */
    public abstract void outputSourceCoverageHtml(Map<String, List<LineCoverage>> lineCoverageMap);
    /**
     * 输出HTML文件
     * @param data 覆盖率数据
     * @param <T> 泛型
     * @param fileName 文件名
     * @param type 文件类型
     */
    protected <T extends Object> void outputHtml(Map<String, T> data, String fileName,DataType type){
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
        try {
            String reportPath = PathUtil.getReportOutPath();

            CopyFiles.copy(new File(reportPath));
            if(CopyFiles.isExistsTemplateFromJar()){
                cfg.setClassLoaderForTemplateLoading(CopyFiles.class.getClassLoader(),"template/");
            }else{
                String templatePath = PathUtil.getJavaResourcePath("");
                File reportDir = new File(reportPath);
                if (!reportDir.exists()) {
                    reportDir.mkdirs();
                }
                cfg.setDirectoryForTemplateLoading(new File(templatePath));
            }

            cfg.setDefaultEncoding("UTF-8");
            String tempName="index.ftl";
            if(type==DataType.CLASS || type==DataType.PACKAGE){
                tempName="index.ftl";
            }else if(type==DataType.METHOD){
                tempName="method.ftl";
            }else if(type==DataType.SOURCE){
                tempName="source.ftl";
            }
            // 加载模板
            Template template = cfg.getTemplate( tempName);
            Writer writer = new FileWriter(new File(reportPath + "/"+fileName));
            template.process(data, writer);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }


    /**
     * 得到源码
     * @param className
     * @return
     */
    protected String getSource(String className){
        List<String> sources=this.sourceMap.get(Crc64.crc64Hex(className));
        StringBuilder source=new StringBuilder();
        for(String line:sources){
            source.append(line).append("\n");
        }
        return source.toString();
    }

    /**
     * 得到源代码的list集合
     * @param className
     * @return
     */
    protected List<String> getSourceList(String className){
        List<String> sources=this.sourceMap.get(Crc64.crc64Hex(className));
        return sources;
    }

    /**
     * 读取所有的源文件
     * @param sourceFile
     */
    protected void readSourceAll(String sourceFile){
        File tempFile=new File(sourceFile);
        if(tempFile.isDirectory()){
            File[] files = tempFile.listFiles();
            if (files != null) {
                for (File file : files) {
                    readSourceAll(file.getAbsolutePath());
                }
            }
        }else{
            if(sourceFile.indexOf(".java")!=-1) {
                readSource(sourceFile);
            }
        }
    }
    /**
     * 给源代码染色
     * @param sourceFile
     */
    protected void readSource(String sourceFile){
       List<String> sourceList=FileLineReaderUtil.readLine(new File(sourceFile));
       String className=readClassName(sourceList);
       sourceMap.put(Crc64.crc64Hex(className),sourceList);
    }
    /**
     * 创建包的目录
     * @param packageName
     */
    protected void createPackageDir(String packageName){
        String reportPath = PathUtil.getReportOutPath();
        String packageDir=reportPath+"/"+packageName;
        File reportDir = new File(packageDir);
        if (!reportDir.exists()) {
            reportDir.mkdirs();
        }
    }
    protected String getPackageName(String key){
        int index=key.lastIndexOf(".");
        return key.substring(0,index);
    }
    protected String getClassName(String key){
        int index=key.lastIndexOf(".");
        return key.substring(index+1);
    }

    /**
     * 读取源文件中的类名
     * @param source
     * @return
     */
    private String readClassName(List<String> source){
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<source.size();i++){
            if(source.get(i).indexOf("package")==-1 &&
                    source.get(i).indexOf("class")==-1 &&
                    source.get(i).indexOf("interface")==-1){
                continue;
            }
            sb.append(source.get(i));
        }
        String packageName=null;
        String className=null;
        int packageIndex=sb.indexOf("package");
        int end=sb.indexOf("import");
        int packageEnd=sb.indexOf(";");
        if(packageName==null && (packageIndex!=-1 && packageEnd!=-1)){
            packageName=sb.substring(packageIndex+7,packageEnd);
            packageName=packageName.trim();
        }
        int classIndex=sb.indexOf("class ");
        int offset=5;
        if(classIndex==-1){
            classIndex=sb.indexOf("interface");
            offset=9;
        }
        int classEnd=sb.indexOf("{");
        if(sb.indexOf("implements")!=-1){
            classEnd=sb.indexOf("implements");
        }
        if(sb.indexOf("extends")!=-1){
            classEnd=sb.indexOf("extends");
        }
        className=sb.substring(classIndex+offset,classEnd);
        return packageName+"."+className.trim();
    }


}
